
/**
 * Classify applications for nursery based upon results from paper by
 * M. Olave, V. Rajkovic, M. Bohanec: An application for admission in
   public school systems. In (I. Th. M. Snellen and W. B. H. J. van de
   Donk and J.-P. Baquiast, editors) Expert Systems in Public
   Administration, pages 145-160. Elsevier Science Publishers (North
   Holland)}, 1989.
 * (http://kt.ijs.si/MarkoBohanec/pub/Nursery89.pdf)
 * 
 * @author axp5100
 *
 */

/******************************************************************************
 * 
 * S T A R T    C O N S T A N T    E N U M E R A T I O N S
 *
 ******************************************************************************/
enum PARENTS {
	USUAL(1),PRETENTIOUS(2), GREAT_PRET(3);
	private int code;
	
	private PARENTS(int code){
		this.code = code;
	}
	
	public int getCode(){
		return code;
	}
}

enum HAS_NURS {
	PROPER(1),LESS_PROPER(2), IMPROPER(3), CRITICAL(4), VERY_CRIT(5);
	private int code;
	
	private HAS_NURS(int code){
		this.code = code;
	}
	
	public int getCode(){
		return code;
	}
}
enum FORM {
	COMPLETE(1),COMPLETED(2), INCOMPLETE(3), FOSTER(4);
	private int code;
	
	private FORM(int code){
		this.code = code;
	}
	
	public int getCode(){
		return code;
	}
}
enum CHILDREN {
	ONE(1), TWO(2), THREE(3), MORE(4);
	private int code;
	
	private CHILDREN(int code){
		this.code = code;
	}
	
	public int getCode(){
		return code;
	}
}
enum HOUSING {
	CONVENIENT(1), LESS_CONV(2), CRITICAL(3);
	private int code;
	
	private HOUSING(int code){
		this.code = code;
	}
	
	public int getCode(){
		return code;
	}
}
enum FINANCE {
	CONVENIENT(1),INCONV(2);
	private int code;
	
	private FINANCE(int code){
		this.code = code;
	}
	
	public int getCode(){
		return code;
	}
}
enum SOCIAL {
	NON_PROB(1), SLIGHTLY_PROB(2), PROBLEMATIC(3);
	private int code;
	
	private SOCIAL(int code){
		this.code = code;
	}
	
	public int getCode(){
		return code;
	}
}
enum HEALTH {
	RECOMMENDED(1), PRIORITY(2), NOT_RECOM(3);
	private int code;
	
	private HEALTH(int code){
		this.code = code;
	}
	
	public int getCode(){
		return code;
	}
}
/******************************************************************************
 * 
 * E N D    C O N S T A N T    E N U M E R A T I O N S
 *
 ******************************************************************************/

/******************************************************************************
 * 
 * S T A R T    C L A S S E S
 *
 ******************************************************************************/
/**
 * Nursery school application
 * 
 * Contained within are attributes for instances of nursery school applications
 * 
 */
class Application {
	
	/*
	 * Attributes
	 */
	PARENTS parents;
	HAS_NURS has_nursery;
	FORM form;
	CHILDREN children;
	HOUSING housing; 
	FINANCE finance;
	SOCIAL social;
	HEALTH health;
	
	/*
	 * Static shenanigans to support multiple instance creation methods
	 * calling values() is an expensive operation. If used for large number of
	 * instances, this would cause quite a mess
	 */
	public static PARENTS[] parentValues;
	public static HAS_NURS[] has_nursValues;
	public static FORM[] formValues;
	public static CHILDREN[] childrenValues;
	public static HOUSING[] housingValues;
	public static FINANCE[] financeValues;
	public static SOCIAL[] socialValues;
	public static HEALTH[] healthValues;
	static{
		Application.parentValues = PARENTS.values();
		Application.has_nursValues = HAS_NURS.values();
		Application.formValues = FORM.values();
		Application.childrenValues = CHILDREN.values();
		Application.housingValues = HOUSING.values();
		Application.financeValues = FINANCE.values();
		Application.socialValues = SOCIAL.values();
		Application.healthValues = HEALTH.values();
	}
	/**
	 * Create an application using named constants
	 * @param p		parents
	 * @param h 	has_nurs
	 * @param f 	form  
	 * @param c 	children
	 * @param ho 	housing
	 * @param fi 	finance
	 * @param s 	social
	 * @param he 	health
	 */
	public Application(
		PARENTS p, 
		HAS_NURS h, 
		FORM f, 
		CHILDREN c, 
		HOUSING ho, 
		FINANCE fi,
		SOCIAL s, 
		HEALTH he
	) {
		parents = p;
		has_nursery = h;
		form = f;
		children = c;
		housing = ho;
		finance = fi;
		social = s;
		health = he;
	}
	/**
	  * Create an application using integer values
	 * @param p		parents
	 * @param h 	has_nurs
	 * @param f 	form  
	 * @param c 	children
	 * @param ho 	housing
	 * @param fi 	finance
	 * @param s 	social
	 * @param he 	health
	 */
	public Application(
		int p, 
		int h, 
		int f, 
		int c, 
		int ho, 
		int fi,
		int s, 
		int he
	) {
		// Values are reduced by one to account for zero indexing of values
		parents = Application.parentValues[p-1];
		has_nursery = Application.has_nursValues[h-1];
		form = Application.formValues[f-1];
		children = Application.childrenValues[c-1];
		housing = Application.housingValues[ho-1];
		finance = Application.financeValues[fi-1];
		social = Application.socialValues[s-1];
		health = Application.healthValues[he-1];
	}
}

/**
 * Classifier class
 * @author axp5100
 *
 */
public class Classifier {
	/**
	 * Atrribute weights - scaled by 1000 to remove floating points
	 * Values determined by solving system of linear equations
	 */
	final static int parents = 312;
	final static int has_nursery = 294;
	final static int form = 400;
	final static int children = 406;
	final static int housing = 128;
	final static int finance = -100;
	final static int social = 244;
	final static int health = 26;
	
	/**
	 * Classify an application using predetermined weights
	 * 
	 * @param app Application to classify
	 * @return Numerical classification of application scaled up by 1000
	 */
	public static int classify(Application app)
	{
		return 	(parents * app.parents.getCode()) +
				(has_nursery * app.has_nursery.getCode()) +
				(form * app.form.getCode()) + 
				(children * app.children.getCode()) +
				(housing * app.housing.getCode()) +
				(finance * app.finance.getCode()) +
				(social * app.social.getCode()) + 
				(health * app.health.getCode());
	}
	
	/**
	 * Print classification of application in decimal form
	 * 
	 * @param app Application to classify
	 */
	public static void decimalClassify( Application app ) {
		// Classify application
		int classification = classify(app);
	
		// Decimal portion is expressed as an expansion of each power of ten
		String decimalPart = "." + 	(classification%1000)/100 + 
				(classification%100)/10	+ 
				((classification%10)>0?(classification%10):"") ;
		System.out.println( classification/1000 + decimalPart );

	}
	
	public static void main( String args[] ) {
		
		// Create new application using named constants
		Application app = new Application(
				PARENTS.PRETENTIOUS,
				HAS_NURS.IMPROPER,
				FORM.COMPLETE,
				CHILDREN.ONE,
				HOUSING.CONVENIENT,
				FINANCE.CONVENIENT,
				SOCIAL.NON_PROB,
				HEALTH.RECOMMENDED
				);
		if(classify(app) == 2.61*1000 ) {
			decimalClassify(app);
		}
		else {
			System.out.println("Error for application 1");
		}
		
		// Create new app using integer values
		app = new Application(1,4,1,4,3,2,3,2);
		if(classify(app) == 4.48*1000) {
			decimalClassify(app);
		}
		else {
			System.out.println("Error for application 2");
		}
				
	}
}
